Imports System.Globalization
Imports System
Imports System.Data
Imports System.Collections
Imports System.Reflection
Imports System.Activator
Imports System.Type
Imports System.Collections.Generic

''' <summary>
''' DataObject is the foundation of a BizBlox architecture. Actual data classes 
''' inherit from this. Use the Builder tool to auto generate these inherited
''' classes.
''' </summary>
''' <remarks>
''' The DataObject is designed to be inherited based on a simple
''' pattern as explained in exampleclient.vb code. This object 
''' manages persistance through load and save methods.
''' Remember: the constructor is lazy and will only hit the database when
''' explicitly requested.
''' 
''' </remarks>
''' <history>
''' 	[cinj]	1/09/2004	Created
''' </history>
Public MustInherit Class DataObject
    Inherits System.MarshalByRefObject

    'object handles
    Protected m_objDA As DataAccess

    'Data mapping management 
    Private m_uid As System.Guid
    Private m_commitUpdateContext As Boolean
    Private m_touched As Boolean = False
    Private m_partiallyLoaded As Boolean = False
    Private m_linkedTables As New Hashtable
    Private m_linkedColumns As New Hashtable
    Private m_touchedColumns() As Boolean
    Private m_loadedColumns() As Boolean

    Public Enum Columns
        DummyColumn
    End Enum

#Region "Caching engine"

    Private Shared m_typeMap As New Dictionary(Of String, DOPropertyHelper)

    Private Shared Function CacheAttributes(ByVal t As Type) As DOPropertyHelper
        Dim h As DOPropertyHelper = Nothing
        If Not m_typeMap.TryGetValue(t.ToString(), h) Then
            SyncLock m_typeMap
                h = New DOPropertyHelper(t)
                m_typeMap.Add(h.DOTypeName, h)
            End SyncLock
        End If
        Return h
    End Function

    Private m_localPropertyHelper As DOPropertyHelper = DataObject.CacheAttributes(Me.GetType())
    Public Function GetPropertyHelper() As DOPropertyHelper
        Return m_localPropertyHelper
    End Function

#End Region

#Region "Constructors"

    ''' <summary>
    ''' Create a new object which has a insert context
    ''' The 'context' is determined by the initialisation of the object
    ''' if you initialise without a GUID then the object doesn't map to 
    ''' a row and therefore will require data properties set.
    '''
    ''' Setting the ID property of a DataObject will not change load, save or delete. 
    ''' it is actually just passive READ ONLY.
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	26/08/2004	Created
    ''' </history>

    Public Sub New()
        Dim pHelper As DOPropertyHelper = Me.GetPropertyHelper()
        Me.InitializeTouchedAndLoaded()
        m_commitUpdateContext = False
        m_objDA = New DataAccess
        m_uid = System.Guid.NewGuid()
        Me.GetType().InvokeMember(pHelper.PrimaryKeyName, BindingFlags.NonPublic Or BindingFlags.SetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, Me, New Object() {m_uid})
        m_touched = False
    End Sub



    ''' <summary>
    ''' Create an object from a database lookup - has an update context
    ''' </summary>
    ''' <param name="uid"></param>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	26/08/2004	Created
    ''' </history>

    Public Sub New(ByVal uid As Guid)
        Me.InitializeTouchedAndLoaded()
        m_commitUpdateContext = True
        m_objDA = New DataAccess
        m_uid = uid
    End Sub

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="uid">ID to retrieve</param>
    ''' <param name="load">Determines if we load the DataObject immediately</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal uid As Guid, ByVal load As Boolean)
        Me.InitializeTouchedAndLoaded()
        m_commitUpdateContext = True
        m_objDA = New DataAccess
        m_uid = uid
        If load Then
            Me.Load()
        End If
    End Sub



    Public Sub New(ByRef baseCol As DOCollection, ByVal index As Integer)
        Me.InitializeTouchedAndLoaded()
        m_commitUpdateContext = True
        m_objDA = New DataAccess
        'Load the UID when loading from the dataset
        m_uid = System.Guid.Empty
        Load(baseCol.ActiveDataSet, index)
    End Sub

    Public Sub New(ByVal dr As DataRow)
        Me.InitializeTouchedAndLoaded()
        m_commitUpdateContext = True
        m_objDA = New DataAccess
        'Load the UID when loading from the dataset
        m_uid = System.Guid.Empty
        Load(dr, Nothing)
    End Sub

    Public Sub New(ByVal dr As DataRow, ByVal selections() As DOSelection)
        Me.InitializeTouchedAndLoaded()
        m_commitUpdateContext = True
        m_objDA = New DataAccess
        'Load the UID when loading from the dataset
        m_uid = System.Guid.Empty
        Load(dr, selections)
    End Sub


#End Region


#Region "Touched and Loaded arrays"

    Private Sub InitializeTouchedAndLoaded()
        Me.m_touched = False
        Dim upperLimit As Integer = Me.GetPropertyHelper().PropertyInfoCount - 1
        'VB uses upper limit index when declaring arrays (retarded)
        Me.m_touchedColumns = New Boolean(upperLimit) {}
        Me.m_loadedColumns = New Boolean(upperLimit) {}
        For i As Integer = 0 To upperLimit
            Me.m_touchedColumns(i) = False
            Me.m_loadedColumns(i) = False
        Next
    End Sub

    Private Sub ClearTouched()
        Me.m_touched = False
        For i As Integer = 0 To Me.m_touchedColumns.Length - 1
            Me.m_touchedColumns(i) = False
        Next
    End Sub

#End Region

#Region "Overrides"
    Public MustOverride Function UidColumn() As [Enum]
    Public MustOverride Function TableName() As String
#End Region


#Region "Data Methods"


#Region "Save"


    ''' <summary>
    ''' Commit the object (or its changes) to the database
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	26/08/2004	Created
    ''' </history>

    Public Sub Save()
        If m_touched = True Then
            If ExistsInTable() Then
                Update(Nothing)
            Else
                Insert(Nothing)
                m_commitUpdateContext = True
            End If
            Me.ClearTouched()
        End If
    End Sub


    ''' <summary>
    ''' Commit the object (or its changes) to the database
    ''' </summary>
    ''' <param name="tObj">Optional: pass in the transaction object</param>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	26/08/2004	Created
    ''' </history>

    Public Sub Save(ByVal tObj As DOTransaction)
        If m_touched = True Then
            If ExistsInTable() Then
                Update(tObj)
            Else
                Insert(tObj)
                m_commitUpdateContext = True
            End If
            Me.ClearTouched()
        End If
    End Sub

#End Region

#Region "Delete"

    ''' <summary>
    ''' Delete the object from the database.
    ''' </summary>
    ''' <param name="tObj">Optional: pass in the transaction object</param>
    ''' <remarks>
    ''' This item will be able to be saved again if any property is touched after deleting it. 
    ''' Requires appropriate database constraints to be fulfilled or a DOException will be thrown
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	26/08/2004	Created
    ''' </history>

    Public Sub Delete(ByVal tObj As DOTransaction)
        Dim sqlStr As String
        sqlStr = "delete from " & TableName() & " where " & UIDColumnName() & "='" & m_uid.ToString() & "'"
        If tObj Is Nothing Then
            m_objDA.RunSQL(sqlStr)
        Else
            tObj.AddSQL(sqlStr)
        End If
        Me.ClearTouched()
        m_commitUpdateContext = False
    End Sub



    ''' <summary>
    ''' Delete the object from the database.
    ''' </summary>
    ''' <remarks>
    ''' This item will be able to be saved again if any property is touched after deleting it. 
    ''' Requires appropriate database constraints to be fulfilled or a DOException will be thrown
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	26/08/2004	Created
    ''' </history>
    Public Sub Delete()
        Dim sqlStr As String
        sqlStr = "delete from " & TableName() & " where " & UIDColumnName() & "='" & m_uid.ToString() & "'"
        m_objDA.RunSQL(sqlStr)
        Me.ClearTouched()
        m_commitUpdateContext = False
    End Sub

#End Region


#Region "Loading"


    ''' <summary>
    ''' Load the object from the database.
    ''' The DataObject's UID must already be set.
    ''' Performs a SELECT * and loads all columns.
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	26/08/2004	Created
    ''' </history>
    Public Sub Load()
        Dim sqlStr As String
        Dim tmpds As DataSet
        sqlStr = "select * from " & TableName() & " where " & UIDColumnName() & "='" & m_uid.ToString() & "'"
        tmpds = m_objDA.RunSQLReturnDataSet(sqlStr)
        Load(tmpds, 0)
    End Sub

    ''' <summary>
    ''' Loads only the specified columns of a DataObject.
    ''' The DataObject's UID must already be set.
    ''' This is an alternative to the default Load, which
    ''' loads all columns (SELECT *).
    ''' The UID column is always loaded.
    ''' </summary>
    ''' <param name="columns"></param>
    ''' <remarks></remarks>
    Public Sub Load(ByVal ParamArray columns() As [Enum])
        Dim sqlStr As String
        Dim tmpds As DataSet
        sqlStr = "select "
        Dim selections As New List(Of DOSelection)
        'Add UID column automatically
        sqlStr &= Me.UIDColumnName()
        selections.Add(New DOSelection(Me, Me.UidColumn))
        'Add other selected columns
        For i As Integer = 0 To columns.Length - 1
            If columns(i).ToString() <> Me.UIDColumnName() Then
                sqlStr &= ", " & columns(i).ToString()
                selections.Add(New DOSelection(Me, columns(i)))
            End If
        Next
        sqlStr &= " from " & TableName() & " where " & UIDColumnName() & "='" & m_uid.ToString() & "'"
        tmpds = m_objDA.RunSQLReturnDataSet(sqlStr)
        Load(tmpds.Tables(0).Rows(0), selections.ToArray())
    End Sub

    ''' <summary>
    ''' Load the object from the dataset.
    ''' </summary>
    ''' <remarks>
    ''' Column names must exactly match the property names of the class being mapped.
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	26/08/2004	Created
    '''     [cinj]  28/01/2008  Refactor seperated the concern of population from the dataset from database load.
    '''     [cinj]  10/02/2009  Changed typecasting to be coalesced by the runtime
    ''' </history>
    Private Sub Load(ByRef tmpds As DataSet, ByVal index As Integer)
        Dim pHelper As DOPropertyHelper = Me.GetPropertyHelper()
        If (tmpds.Tables.Count > 0) And (tmpds.Tables(0).Rows.Count > index) Then
            Load(tmpds.Tables(0).Rows(index), Nothing)
        Else
            m_commitUpdateContext = False
            Throw New DOException("Failed load. DataObject does not exist in the database.")
        End If
        Me.ClearTouched()
    End Sub


    Private Sub Load(ByVal dr As DataRow, ByVal selections() As DOSelection)
        If selections Is Nothing OrElse (selections.Length = 1 AndAlso selections(0).TableName = Me.TableName AndAlso selections(0).IsAllColumns) Then
            'This is the standard method used when using Find, Fetch, FindRange, FetchRange etc.
            'to completely populate all columns of a DataObject
            LoadAllColumns(Me, dr)
        Else
            'This is a more customizable method, which allows you to populate only the columns selected
            'in the query. It also allows you to select columns from other tables into "linked" table objects
            'that can also be only partially populated. It can also select the results of functions, such as
            'a SUM(column) or AVG(column) into linked columns using "SELECT column AS" functionality
            For i As Integer = 0 To selections.Length - 1
                Dim aObj As DataObject = Nothing
                Dim s As DOSelection = selections(i)
                If s.FunctionName = DOFunction.None_ Then
                    'Retrieve a column from the datarow into an Object
                    If s.TableName = Me.TableName Then
                        'Retrieve into current Object
                        aObj = Me
                    Else
                        'Retrieve into a linked Object
                        aObj = Me.GetOrCreateLinkedTable(s)
                    End If
                    If s.IsAllColumns Then
                        LoadAllColumns(aObj, dr)
                    Else
                        Dim pHelper As DOPropertyHelper = aObj.GetPropertyHelper()
                        Dim myInfo As DOInfo = pHelper.GetInfo(s.ColumnName)
                        LoadColumn(aObj, dr, myInfo)
                    End If
                Else
                    'This selection involves a function call, and the creation of a new linked column.
                    'Load this column into a special hashtable. For example, we may do 
                    'SELECT LunchOrder.StudentID, COUNT(LunchOrder.LunchID) AS count1
                    Dim columnName As String = s.GetResultColumnName()
                    Me.m_linkedColumns.Add(columnName, DataSetUtils.GetDataRowObject(dr, columnName))
                End If
            Next
        End If
        m_commitUpdateContext = True
        Me.ClearTouched()
    End Sub

    Private Shared Sub LoadAllColumns(ByVal aObj As DataObject, ByVal dr As DataRow)
        Dim pHelper As DOPropertyHelper = aObj.GetPropertyHelper()
        For i As Integer = 0 To pHelper.PropertyInfo.Length - 1
            Dim myInfo As DOInfo = pHelper.GetInfo(i)
            DataObject.LoadColumn(aObj, dr, myInfo)
        Next
    End Sub

    Private Shared Sub LoadColumn(ByVal aObj As DataObject, ByVal dr As DataRow, ByVal myInfo As DOInfo)
        If dr.IsNull(myInfo.PropertyName) = True Then
            If myInfo.TypeName = "System.String" Then
                aObj.GetType().InvokeMember(myInfo.PropertyName, BindingFlags.NonPublic Or BindingFlags.SetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, aObj, New Object() {""})
            ElseIf myInfo.TypeName = "System.Guid" Then
                aObj.GetType().InvokeMember(myInfo.PropertyName, BindingFlags.NonPublic Or BindingFlags.SetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, aObj, New Object() {New Guid("00000000-0000-0000-0000-000000000000")})
            Else
                aObj.GetType().InvokeMember(myInfo.PropertyName, BindingFlags.NonPublic Or BindingFlags.SetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, aObj, New Object() {Nothing})
            End If
        Else
            Select Case myInfo.TypeName
                Case "System.DateTime"
                    Dim val As String = CType(dr.Item(myInfo.PropertyName), String)
                    aObj.GetType().InvokeMember(myInfo.PropertyName, BindingFlags.NonPublic Or BindingFlags.SetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, aObj, New Object() {DOFormatter.DateFromString(val)})
                Case "System.Guid"
                    aObj.GetType().InvokeMember(myInfo.PropertyName, BindingFlags.NonPublic Or BindingFlags.SetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, aObj, New Object() {CType(dr.Item(myInfo.PropertyName), System.Guid)})
                    If myInfo.PrimaryKey Then
                        aObj.m_uid = CType(dr.Item(myInfo.PropertyName), System.Guid)
                    End If
                Case Else
                    aObj.GetType().InvokeMember(myInfo.PropertyName, BindingFlags.NonPublic Or BindingFlags.SetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, aObj, New Object() {dr.Item(myInfo.PropertyName)})
            End Select
        End If
        aObj.m_loadedColumns(myInfo.Index) = True
    End Sub


#Region "Derived/Linked tables and columns from query"

    Private Function GetOrCreateLinkedTable(ByVal selection As DOSelection) As DataObject
        Dim aObj As DataObject = Nothing
        If Me.m_linkedTables.ContainsKey(selection.TableName) Then
            aObj = Me.m_linkedTables(selection.TableName)
        Else
            Dim f As New DOFactory(selection.Table.GetType())
            aObj = f.Create(System.Guid.Empty)
            Me.m_linkedTables(selection.TableName) = aObj
        End If
        Return aObj
    End Function

    Public Function GetLinkedTable(ByVal aObj As DataObject) As DataObject
        Return GetLinkedTable(aObj.TableName)
    End Function

    Public Function GetLinkedTable(ByVal tableName As String) As DataObject
        If m_linkedTables.ContainsKey(tableName) Then
            Return m_linkedTables(tableName)
        Else
            Return Nothing
        End If
    End Function

    Public Function GetLinkedColumn(ByVal selectAsColumnName As String) As Object
        If m_linkedColumns.ContainsKey(selectAsColumnName) Then
            Return m_linkedColumns(selectAsColumnName)
        Else
            Return Nothing
        End If
    End Function

    Public Function GetLinkedColumn(ByVal s As DOSelection) As Object
        Return GetLinkedColumn(s.GetResultColumnName())
    End Function


#End Region


#End Region





    ''' <summary>
    ''' copy all properties to a new object of the same type (except the UID)
    ''' </summary>
    ''' <param name="srcObj"></param>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	26/08/2004	Created
    ''' </history>

    Public Sub Copy(ByVal srcObj As Object)
        Dim myPropertyInfo() As PropertyInfo
        Dim tmpCol As String
        myPropertyInfo = Me.GetType().GetProperties()
        For i As Integer = 0 To myPropertyInfo.Length - 1
            tmpCol = myPropertyInfo(i).Name
            If Not tmpCol = UIDColumnName() Then
                Me.GetType().InvokeMember(tmpCol, BindingFlags.NonPublic Or BindingFlags.SetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, Me, New Object() {CType(Me.GetType().InvokeMember(tmpCol, BindingFlags.NonPublic Or BindingFlags.GetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, srcObj, New Object() {}), String)})
            End If
        Next i
    End Sub



    Private Function GetRawProperty(ByVal info As DOInfo) As Object
        Return Me.GetType().InvokeMember(info.PropertyName, BindingFlags.NonPublic Or BindingFlags.GetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, Me, New Object() {})
    End Function

    ''' <summary>
    ''' Prep output for SQL commit
    ''' 
    ''' </summary>
    ''' <param name="info"></param>
    ''' <returns></returns>
    ''' <remarks>
    ''' 	[cinj]	29/07/2004	Created
    '''     [cinj]  10/02/2009  Updated to take DOInfo and use native tostring operations
    ''' </remarks>
    Private Function ParseProperty(ByVal info As DOInfo, ByVal rawObj As Object) As String
        Select Case info.TypeName
            Case "System.Guid"
                Dim xStr As String = CType(rawObj, System.Guid).ToString()
                If xStr = "00000000-0000-0000-0000-000000000000" Then Return Nothing
                Return xStr
            Case "System.Boolean"
                If CType(rawObj, Boolean) = True Then
                    Return "1"
                Else
                    Return "0"
                End If
            Case "System.DateTime"
                Dim dtStr As String = DOFormatter.DateToString(CType(rawObj, DateTime))
                If dtStr = "0001-01-01 00:00:00Z" Then Return Nothing
                Return dtStr
            Case "System.String"
                If rawObj Is Nothing Then Return ""
                Return CType(rawObj, String)
            Case Else
                If rawObj Is Nothing Then Return Nothing
                Return rawObj.ToString()
        End Select
    End Function

#Region "Insert and Update"

#Region "Insert"

    Private Sub Insert(Optional ByRef tObj As DOTransaction = Nothing)
        Dim sqlStr As String
        Dim pHelper As DOPropertyHelper = Me.GetPropertyHelper()
        sqlStr = "insert into [" & TableName() & "]("
        sqlStr &= GetInsertColumnNamesSql(pHelper)
        sqlStr = sqlStr & ") values ("
        sqlStr &= GetInsertColumnValuesSql(pHelper)
        sqlStr = sqlStr & ")"
        If tObj Is Nothing Then
            m_objDA.RunSQL(sqlStr)
        Else
            tObj.AddSQL(sqlStr)
        End If
    End Sub

    Private Function GetInsertColumnNamesSql(ByVal pHelper As DOPropertyHelper) As String
        Dim sqlStr As String = ""
        Dim i As Integer
        Dim startedFlag As Boolean
        'set up insert sql here
        startedFlag = False
        For i = 0 To pHelper.PropertyInfo.Length - 1
            If Not pHelper.PropertyInfo(i).Identity Then
                If startedFlag = False Then
                    startedFlag = True
                Else
                    sqlStr = sqlStr & ","
                End If
                sqlStr = sqlStr & pHelper.PropertyInfo(i).PropertyName
            End If
        Next i
        Return sqlStr
    End Function

    Private Function GetInsertColumnValuesSql(ByVal pHelper As DOPropertyHelper) As String
        Dim sqlStr As String = ""
        Dim startedFlag As Boolean = False
        For i As Integer = 0 To pHelper.PropertyInfo.Length - 1
            Dim myInfo As DOInfo = pHelper.PropertyInfo(i)
            If Not myInfo.Identity Then
                If startedFlag = False Then
                    startedFlag = True
                Else
                    sqlStr = sqlStr & ","
                End If
                Dim rawVal As Object = GetRawProperty(myInfo)
                Me.ValidateColumn(myInfo, rawVal)
                Dim tmpVal As String = ParseProperty(myInfo, rawVal)
                If tmpVal Is Nothing Then
                    sqlStr &= " NULL "
                Else
                    If myInfo.TypeName = "System.String" Then
                        sqlStr &= "N"
                    End If
                    sqlStr &= "'" & DOSecurity.EscapeString(tmpVal) & "'"
                End If
            End If
        Next i
        Return sqlStr
    End Function

#End Region



    Private Sub Update(Optional ByRef tObj As DOTransaction = Nothing)
        Dim tmpVal As String
        Dim rawVal As Object
        Dim sqlStr As String
        Dim pHelper As DOPropertyHelper = Me.GetPropertyHelper()
        Dim myInfo As DOInfo

        sqlStr = "update [" & TableName() & "] set "
        Dim i As Integer
        Dim startedFlag As Boolean

        'set up insert sql here
        startedFlag = False
        For i = 0 To pHelper.PropertyInfo.Length - 1
            If Me.m_touchedColumns(i) = True Then
                myInfo = pHelper.PropertyInfo(i)
                If (Not myInfo.PrimaryKey) And (Not myInfo.Identity) Then
                    If startedFlag = True Then
                        sqlStr = sqlStr & ","
                    Else
                        startedFlag = True
                    End If
                    rawVal = GetRawProperty(myInfo)
                    Me.ValidateColumn(myInfo, rawVal)
                    tmpVal = ParseProperty(pHelper.PropertyInfo(i), rawVal)
                    sqlStr = sqlStr & myInfo.PropertyName & " = "
                    If tmpVal Is Nothing Then 'And (Not tmpCol.StartsWith("s_")) Then
                        sqlStr = sqlStr & " NULL "
                    Else
                        If myInfo.TypeName = "System.String" Then
                            sqlStr &= "N"
                        End If
                        sqlStr &= "'" & DOSecurity.EscapeString(tmpVal) & "'"
                    End If
                End If
            End If
        Next i
        'key the update against this UID.
        'refactor to cache the pk from the previous loop!
        sqlStr = sqlStr & " where " & pHelper.PrimaryKeyName & "='" & Me.ParseProperty(pHelper.GetInfo(pHelper.PrimaryKeyName), Me.GetRawProperty(pHelper.GetInfo(pHelper.PrimaryKeyName))) & "'"
        If tObj Is Nothing Then
            m_objDA.RunSQL(sqlStr)
        Else
            tObj.AddSQL(sqlStr)

        End If
    End Sub


#End Region




    Private Sub ValidateColumn(ByVal myInfo As DOInfo, ByVal rawVal As Object)
        'Validator goes here
        For j As Integer = 0 To myInfo.Validators.Count - 1
            myInfo.Validators(j).Validate(rawVal)
        Next
    End Sub



#End Region

#Region "Special Properties Methods"
    'get this object UID
    <Obsolete("The function should not be used any longer. The design of BizBlox is shifting in future releases to allow other data types as primary keys")> _
    Public Function Uid() As Guid
        Return m_uid
    End Function


    ''' <summary>
    ''' Does this object exist in the table it maps to?
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	26/08/2004	Created
    ''' </history>

    Public Function ExistsInTable() As Boolean
        Return m_commitUpdateContext
    End Function


    ''' <summary>
    ''' Determine if a value is EQUIVALENT to a null.
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	26/08/2004	Created
    ''' </history>

    Public Shared Function IsNull(ByVal dtVal As DateTime) As Boolean
        If dtVal = Nothing Then Return True
        Dim dtStr As String = DOFormatter.DateToString(dtVal)
        If dtStr = "0001-01-01 00:00:00Z" Then Return True
        Return False
    End Function


    ''' <summary>
    ''' Determine if a value is EQUIVALENT to a null.
    ''' </summary>
    ''' <param name="gVal"></param>
    ''' <returns></returns>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	26/08/2004	Created
    ''' </history>

    Public Shared Function IsNull(ByVal gVal As Guid) As Boolean
        If gVal.ToString() = "00000000-0000-0000-0000-000000000000" Then Return True
        Return False
    End Function


    ''' <summary>
    ''' Get the column name of the Primary Key (UniqueID)
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	26/08/2004	Created
    ''' </history>

    <Obsolete("This property should not be used by client code any longer. the DOInfo should be used instead.")> _
    Public Function UIDColumnName() As String
        Return UidColumn().ToString()
    End Function


    ''' <summary>
    ''' Get a data transfer object of the type defined by [toType] and populate it from
    ''' the data contained in the DataObject
    ''' </summary>
    ''' <param name="toType"></param>
    ''' <returns></returns>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	19/08/2004	Created
    ''' </history>

    Public Function GetTO(ByVal toType As Type) As Object
        Dim myPropertyInfo() As PropertyInfo = Me.GetType().GetProperties()
        Dim toObj As Object
        toObj = Activator.CreateInstance(toType)
        For i As Integer = 0 To myPropertyInfo.Length - 1
            If Not toType.GetProperty(myPropertyInfo(i).Name) Is Nothing Then
                toType.InvokeMember(myPropertyInfo(i).Name, BindingFlags.NonPublic Or BindingFlags.SetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, toObj, New Object() {Me.GetType().InvokeMember(myPropertyInfo(i).Name, BindingFlags.NonPublic Or BindingFlags.GetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, Me, New Object() {})})
            End If
        Next i
        Return toObj
    End Function


    ''' <summary>
    ''' Accept a transfer object (which inherits from DOTO) to populate this DataObject
    ''' </summary>
    ''' <param name="toObj"></param>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	19/08/2004	Created
    ''' </history>

    Public Sub AcceptTO(ByVal toObj As Object)
        Dim myPropertyInfo() As PropertyInfo = Me.GetType().GetProperties()
        For i As Integer = 0 To myPropertyInfo.Length - 1
            If Not toObj.GetType().GetProperty(myPropertyInfo(i).Name) Is Nothing Then
                Try
                    If Not myPropertyInfo(i).Name = Me.UIDColumnName() Then
                        Me.GetType().InvokeMember(myPropertyInfo(i).Name, BindingFlags.NonPublic Or BindingFlags.SetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, Me, New Object() {toObj.GetType().InvokeMember(myPropertyInfo(i).Name, BindingFlags.NonPublic Or BindingFlags.GetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, toObj, New Object() {})})
                    End If
                Catch ex As Exception

                End Try
            End If
        Next i
    End Sub

    Public ReadOnly Property CommitUpdateContext() As Boolean
        Get
            Return Me.m_commitUpdateContext
        End Get
    End Property

#End Region

#Region "SubClass Utility Method"

    ''' <summary>
    ''' all subclassed property SET methods *must* call this 
    ''' subroutine to flag that the object has changed and 
    ''' needs to be committed to the database.
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[cinj]	26/08/2004	Created
    ''' </history>

    Protected Sub Touch(ByVal index As Integer)
        m_touched = True
        Me.m_touchedColumns(index) = True
    End Sub


#End Region

End Class

